原文链接:https://twin.sci-hub.tw/5813/1d94a680974a94dd20b6e8fa7196851f/zhu2016.pdf#view=FitH

本文发表在SACMAT’16。

背景知识

在Web安全领域中,越权漏洞是一种比较常见的漏洞。尽管经验表明许多应用程序容易受到此类攻击,但是攻击者很难在第一次就成功攻击。所以本文提出了一种通过分析应用程序源代码的方式来检测一些访问控制攻击之间。并且作者测试了两个开源的Web应用程序来评估方法的误报(false positive and negative)。

Listing 1所示的是一个网上银行程序的资金转移函数。如果if条件语句执行失败,就说明用户正在尝试从一个账户中转移不属于ta的资金:

1

作者认为,对于大多数网上银行应用程序来说,这是攻击的有力证据,因为它不太可能是由用户的无意操作造成的。

所以作者提出了一种运行时检测越权攻击行为的保护机制,通过在应用程序源代码中插入检测的代码来实现,如Listing 2所示:

2

实现Listing 2中的检测逻辑需要保证三件事:

  1. 该检测代码应该能够自动化插入;
  2. 不改变原程序的执行语义;
  3. 对程序性能的影响应该忽略不计。

为了解决这个问题,一个重要的问题就是什么情况下应该判定用户的一个访问行为是潜在的越权攻击行为?为此,作者将通过非可见的链接对某网页进行强制访问的行为定义为Forced Browsing

但是在一些情况下,一些forced browsing行为也可能是用户的无危害行为,比如对一个受权限验证保护的页面,在用户的session过期之后,用户可能会进行一个refresh操作,那在这种情况下,就不能说这个行为是存在恶意的攻击行为。这种时候其实可以通过检查用户的访问次数来判定该行为的性质,因为在绝大多数的情况下攻击者会尝试多次攻击。

所以本文的目标就是找到潜在的forced browsing事件,并且在源代码中插入检测代码来保护并提醒网站维护人员。

设计与实现

本文的主要思路分为以下四步:

  1. 首先要确定源代码中属于访问控制检测部分的代码;
  2. 建立一个网站的导航图,node表示一个网页,edge表示连接页面之间的URL;
  3. 识别那些受访问控制逻辑的页面,这些页面只有在通过了访问权限检查之后才能显示;
  4. 这些页面肯定会实现访问失败时的执行逻辑,所以我们要在这些访问失败时执行的逻辑里添加保护代码去报告该类forced browsing事件。

识别访问控制逻辑

首先需要确定敏感操作,作者在论文中将敏感操作称为Security Sensitive Operation(SSO),并且认为SQL操作为SSO。

对于这类敏感操作,应用程序肯定会对用户进行验证来确保当前用户有操作权限(验证机制是否充分不在考虑范围之内),所以对于一个给定的SSO,access control check一定满足这些要求:

  1. access control check一定在程序的入口点到SSO的执行路径上;

  2. 当权限验证失败时,必须能够更改执行路径以拒绝对SSO的访问,一般有两种情况,一种是if条件分支,如Lising 1所示;另一种是通过抛出异常或是终止程序执行:

    3

识别可能被强制访问的页面

构建粗略的导航图

首先需要构建程序的导航图,下图是Wheatblog的部分导航图:

4

上图的边有两种情况:

  1. _conditional link_,指那些只有通过了访问控制检查才会显式出现的链接,在上图中是虚线的边;
  2. _unconditional link_,指那些不需要通过访问控制检查就能访问的链接,在上图中是实线的边。

图2中虚线框中的页面指那些只有以管理员身份登录时才能正常访问的页面,这些页面是可能被用户强制访问的,即对这些页面的访问是涉及权限检查的。

为了构建这样的导航图,作者从源代码中提取链接,其中最难解决的是动态生成url的问题,如下图所示,可以看到Listing 4中的链接就是静态的,是可以从源代码中提取的,但是Listing 5的链接就是动态的,由$lang变量决定,在一些更复杂的情况下,可能由if条件语句控制生成:

5

本篇论文中,作者并没有尝试去解决这个问题,仅仅提取了静态链接。作者采取的方法很简单,定义一个worklist queue,然后从index.php页面开始访问,提取出其中的静态链接,比如admin/edit_post.php,那就记为二元组(index.php, admin/edit_post.php)并将admin/edit_post.php加入worklist queue,如果页面中没有指向下一个页面的链接,则记为(index.php, _),如果静态链接是指向外部网站的外链,则丢弃。接着从worklist queue中pop出index.php,将index.php比较为已访问,从worklist queue中取出下一个节点接着访问。

作者定义了两个集合:

  1. critical global variables,简记为CV,是从if条件语句和抛出异常的函数中提取出来的;
  2. security critical global variables,简记为SCV,参与访问控制权限决策的CV。

比如对于下图有:CV = { $_SESSION['privilege'] }SCV = { $_SESSION['privilege'] }

6

为了找出导航图中的conditional links,文中是这样做的:

  1. 为SSO找出相关的SCV,记为SCV_ssos

  2. 对于粗略导航图中的每一条边l(链接),将其作为一条候选的conditional link

    a. 为边l提取出相关的CV,记为CV_link_l

    b. 将两个集合SCV_ssosCV_link_l进行比较,如果有交集,则说明边l是conditional link。

识别候选页面

接着需要找出插入检测代码的候选页面,需要满足两个要求:

  1. 该页面必须包含至少一个SSO操作;
  2. 该页面只能通过conditional link进行访问。

检测代码生成

在对控制权限检查通过之后,就可以执行一些敏感操作,如果检测失败,则说明存在越权行为,所以代码应该插入在检测失败的逻辑,Listing 9是作者给出的一个例子(但是这个例子是不是不太合适,因为检测通过,执行了SSO操作之后还是会继续执行后面的代码的,如果在后面插入sensor,可能需要额外的检测?):

7

if-else逻辑很好理解,检测代码应在else逻辑中:

8

switch语句:

9

对于函数内的抛出异常和异常终止的情况,则应该在终止程序或是抛出异常之前插入检测代码:

10

总体评价

本篇工作的目的是对PHP应用程序进行一个runtime的防御,检测用户的可疑行为,总的思路并不复杂。在实验评估环节,作者仅仅对两个适配性比较高的PHP应用程序进行了测评,还是比较tricky的。实验中存在一些误报,主要分为两类:

  1. false positive,将用户的正常行为误报为恶意的越权行为,作者认为主要是因为将一些unconditional边误认为conditional边了;
  2. false negative,漏报了一些恶意的越权行为,这主要是因为将conditional边漏掉了。

而且作者在构造粗略的导航图时只提取了静态的链接,丢弃了那些动态链接,这样会造成一些恶意行为的漏报。